import {Cask} from 'cascadium';

import Category from '../src/Category';
import Voucher from '../src/Voucher';

export const mergeDetailedCategory = (category, categoryDetail) => {

  category.unnestTo().mergeFrom(categoryDetail, {
    sourceKey: elem => elem.curr.code.split('.')[0],
    targetKey: 'curr.code',
    mergeFunc: ({source, target}) => {
      source.move(target, {withProto:false, withPath: true, pushSide: 'TAIL'});
    }
  });
  
  const newSubs = category.unnestTo();
  for (let i = newSubs.length - 1 ; i >= 0; i--) {
    newSubs[i].attr.level = 'CATEGORY';
  }

}

export const mergeDetailedJournal = (journalEntries, journalDetail) => {

  journalEntries.mergeFrom(journalDetail, {
    mergeKey: 'voucherID',
    mergeFunc: ({source, target}) => {
      target.curr = source.curr;
    }
  });

}

export const amendCategory = (category, journalEntries) => {

  let newCreatedCategories = 0;

  const voucherEntryMap = journalEntries
    .grip('curr.code');
  const voucherEntryMapKeys = voucherEntryMap.keys();

  for (let i = voucherEntryMapKeys.length - 1; i >= 0; i--) {

    const categoryEndEntryMap = category.unnestTo()
    .grip('curr.code');
    const categoryEndEntryMapKeys = categoryEndEntryMap.keys()

    const voucherKey = voucherEntryMapKeys[i];
    if (!categoryEndEntryMapKeys.includes(voucherKey)) {
      const [{curr}] = voucherEntryMap.get(voucherKey);
      
      const trimmedVoucherKey = voucherKey.includes('.') ? voucherKey.split('.')[0] : voucherKey;

      let upperCate;

      const upperCateKey = categoryEndEntryMapKeys
        .filter(key => trimmedVoucherKey.startsWith(key))
        .sort((a, b) => a.length - b.length)
        .pop();

      // 尽管很难承认，但是真的存在一级科目在当年的余额表中不存在的情况，这种情况下
      if (upperCateKey === undefined) {
        const code = trimmedVoucherKey.slice(0, 4);
        upperCate = new Category({code, name:`新建科目-${code}`});
        upperCate.attr.amended = true;
        upperCate.attr.level = 'CATEGORY';

        upperCate.move(category, {withProto: false, withPath: false, pushSide: 'TAIL'});

        newCreatedCategories += 1;
      } else {
        [upperCate] = categoryEndEntryMap.get(upperCateKey);
      }

      const newCate = new Category({curr});
      newCate.attr.amended = true;
      newCate.attr.level = 'CATEGORY';

      newCate.move(upperCate, {withProto: false, withPath: true, pushSide: 'TAIL'});
      newCreatedCategories += 1;
    } 
  }

  return newCreatedCategories;
}

export const decomposeVouchers = (category, journalVouchers) => {

  const mergedCateEnds = category.unnestTo().all('succ.length', 0);
  const mergedCateEndMap = mergedCateEnds.grip('curr.code').map(([k, [{curr}]]) => [k, curr]);

  // 先为末级科目的条目做个标记
  for (let cateEndEntry of mergedCateEnds) {
    const {curr} = cateEndEntry;
    curr.setRef(cateEndEntry);
    curr.mark = true;
  }

  // 然后将末级科目的curr写入序时账的curr和dest
  for (let v = 0; v < journalVouchers.length; v++) {
    const voucher = journalVouchers[v];
    voucher.decompose();

    for (let entry of voucher.succ) {
      const {curr: {code: currCode}, dest: {code: destCode}} = entry;
      const curr = mergedCateEndMap.get(currCode);
      const dest = mergedCateEndMap.get(destCode);
      Object.assign(entry, {curr, dest});

      entry.attr.level = 'ENTRY';
    }
  }

}

export const recategorize = (category, journalVouchers) => {

  const decompVoucherEntries = new Cask();

  for (let {succ} of journalVouchers) {
    decompVoucherEntries.push(...succ);
  }

  const gatheredByCate = decompVoucherEntries
  .grip('curr.code')
  .grap(([, entries]) => {

    const gatheredByDest = entries
      .grip('dest.code')
      .grap(([, entries]) => {

        const gatheredByDir = entries
        .grip(({accrual}) => accrual.dir())
        .grap(([dir, entries]) => {
          const voucher = Voucher.from(entries, {voucherID: undefined});
          Object.assign(voucher.attr, {level: 'DIRECTION', dir});
          return voucher;
        })

        const voucher = Voucher.from(gatheredByDir, {voucherID: undefined})
        voucher.attr.level = 'DEST_CATE';

        return voucher;
      });
    return Voucher.from(gatheredByDest);
  })
  .flat();
    
  for (let currGroup of gatheredByCate) {

    const flatCurrGroup = currGroup.succ.map(({succ}) => succ).flat();

    expect(new Cask(...flatCurrGroup).every(({curr}) => curr === currGroup.curr));

    for (let destGroup of currGroup.succ) {
      expect(destGroup.succ.every(({dest}) => dest === destGroup.dest));
    }
  }

  const categoryEnds = category.unnestTo().all('succ.length', 0);

  categoryEnds.mergeFrom(gatheredByCate, {
    mergeKey: 'curr.code',
    mergeFunc: ({source:{succ: destCates}, target}) => {
      for (let i = destCates.length - 1; i >= 0; i --) destCates[i].move(target);
    }
  });

  return {
    decompVoucherEntries,
  }

}